﻿/** 
 * @filename:	EditableTable.js
 * @author: 	baboo<istxwang@gmail.com>
 * @description:一个可编辑的列表框架，提供行记录的添加，删除，顺序调整，及高度的自定义序列化操作
 * @version: 1.0.0
 */
$.EditableTable = $({
	__init: function(sets){
		/**
		 * Kit functions
		 */
		var getIndex = function(tr) {
			var trs = $trs();
			for(var i = 0; i < trs.length; i++) if(tr==trs[i]) return i;
			return -1;	
		};
		var getTR = function(e) {
			while (e.tagName != 'TR' && e.tagName != 'HTML') {
				e = e.parentNode;	
			}
			return e.tagName == 'TR' ? e : false;
		};
		var getExternInfo = function (tr) {
			if(sets.externInfo == undefined) return null;
			var ret = [];
			for(var i = 0; i < sets.externInfo.length; i ++) {
				ret.push(sets.externInfo[i].get(tr));
			}
			return ret;
		};
		var fillTR = function (tr, tdsHTML) {	
			for (var i = 0; i < tdsHTML.length; i ++) {
				var td = tr.insertCell(i);
				td.innerHTML = tdsHTML[i];
				if(sets.onTDCreated) sets.onTDCreated(td, i);
			}
		};
		var insertTR = function (tbody, index, tdsHTML, externInfo) {
			var tr = tbody.insertRow(index);
			fillTR(tr, tdsHTML);
			if(sets.onTRCreated) sets.onTRCreated(tr, index);
			if(externInfo) {
				for(var i = 0; i < externInfo.length; i++) {
					sets.externInfo[i].set(tr, externInfo[i]);
				}	
			}
			return tr;
		};
		var hasPositionField = false;
		if(sets.positionElementQueryStr != undefined) {
			var getPositionNum = function (tr) {
				var numstr = $(sets.positionElementQueryStr, tr).html();
				if(numstr.isEmpty()) return -1;
				var ret = parseInt(numstr);
				return isNaN(ret) ? -1 : ret;
			};
			var setPositionNum = function (tr, num) {
				var numE = $(sets.positionElementQueryStr, tr);
				numE.html(String(num));
			}; 
			var getLastPositionNum = function () {
				var trs = $trs(), ret;
				for (var i = trs.length - 1; i >= 0; i--) {
					ret = getPositionNum(trs[i]);
					if (ret != -1) return ret;
				} 
				return 0;
			};
			hasPositionField = true;
        }


        /**
         * 数据行拖放设置
         */
		if(sets.dragElement && $.isFunction(sets.getDragInfo)) {
			var dragE = $E(sets.dragElement);
			var setDrag = function(event){
    				var x = $(event, 'x') + 5, y = $(event, 'y') + 5;
    				dragE.css('left:' + x + 'px;top:' + y + 'px');
			}
			var dragMove = function(event){
    				setDrag(event);
    				$(event, 'stop');
			};
			var startDragTr = null;
			$.observe(document, 'mousedown', (function(event){
					if (['INPUT', 'SELECT', 'A'].include($(event, 'element').tagName)) return;
    				var tr = $(event, 'find', 'tr');
    				if(tr) {
    					if($.isFunction(sets.checkDrag) && !sets.checkDrag(tr)) return;
    					$(event, 'stop');
    					var info = sets.getDragInfo(tr);
        				if(info) {
            					dragE.html(info);
            					startDragTr = tr;
            					setDrag(event);
            					dragE.$('$show');
            					$.observe(document, 'mousemove', dragMove);
        				}
        				
    				} else {

    				}
			}).bind(this));
			$.observe(document, 'mouseup', (function(event){
    				var tr = $(event, 'find', 'tr');
    				if(tr && startDragTr && tr != startDragTr) {
       					var sindex = getIndex(startDragTr);
       					var eindex = getIndex(tr);
       					var delta = eindex - sindex;
       					if(delta) {
          					this[delta<0?'moveToTop':'moveToBottom'](startDragTr, Math.abs(delta));  
       					}
    				}
    				dragE.$('$hide');
    				startDragTr = null;
    				$.stopObserving(document, 'mousemove', dragMove);
			}).bind(this));
		}
		var getID = sets.getID || function(){return -1};

        if(sets.tbody === undefined) throw 'EditableTable want parameter tbody';

        var tbody = sets.tbody, $trs = function(){return tbody.rows};
		var deletedRecordIDs = [];
		var modified = false;
		var setModified = function() {
			if(modified == false) {
				modified = true;
				window.onbeforeunload = function(){return '您修改的数据尚未保存，你确定要离开当然页面嘛？'};
			}	
        };

        var cmd_prefix = sets.cmdPrefix || 'cmd_';
        var cmd_prefix_reg = new RegExp(cmd_prefix + '(\\w+)');
        //命令绑定
        $.observe(tbody, 'click', (function(event) {
            var e = $(event, 'element');
            var classStr = e.className;
            if (classStr.indexOf(cmd_prefix) == -1) {
                return;
            }
            var ma = classStr.match(cmd_prefix_reg);
            if (!ma) return;
            var cmd = ma[1];
            switch (cmd) {
                case 'up' : this.moveToTop(e, 1, event); break;
                case 'down' : this.moveToBottom(e, 1, event); break;
                case 'top' : this.moveToTop(e, undefined, event); break;
                case 'bottom' : this.moveToBottom(e, undefined, event); break;
                case 'remove' : this.remove(e, event); break;
                default :
                if (sets.cmd_handlers && sets.cmd_handlers['do_' + cmd])
                    sets.cmd_handlers['do_' + cmd](e, event);
            }
        }).bindAsEventListener(this));

		this.getDeletedIDs = function () { return deletedRecordIDs };
		this.remove = function (e, event) {
			if(event) $(event, 'stop');
			var tr = getTR(e), index = getIndex(tr);
			if (sets.onRemove) sets.onRemove(getID(tr), tr);
			setModified();
			deletedRecordIDs.push(getID(tr));
			if(!hasPositionField) {
				tbody.deleteRow(index);
				return;
			}
			var posNum = getPositionNum(tr);
			tbody.deleteRow(index);
			if(posNum == -1) return;
			var trs = $trs();
			for(var i = index; i < trs.length; i ++) {
				if(getPositionNum(trs[i]) != -1) {
					setPositionNum(trs[i], posNum);
					posNum++;	
				}
			}
		};
		this.append = function (tdsHTML) {
			var newTR = insertTR(tbody, -1, tdsHTML);
			if (hasPositionField) { 
				var lastPosNum = getLastPositionNum();
				setPositionNum(newTR, lastPosNum + 1);	
            }
            setModified();
		};
		this.getRowByInfo = function (infoValue, getInfo) {
			var trs = $trs();
			for (var i = 0; i < trs.length; i++) {
				if (infoValue == getInfo(trs[i])) return trs[i];	
			}
			return null;
		};
		this.getRowById = function (id) {
			return this.getRowByInfo(id, getID);
		};
		var lastPositionOperatingElement = null, lastPositionOperatingStyle = null;
		var positionOperating = function (e, direction) {
			var activeClass = (sets.positionOperatingStyle || {})[direction];
			if (activeClass) {
				if (lastPositionOperatingElement) {
					$.Dom._class(lastPositionOperatingElement, '-' + lastPositionOperatingStyle);
				}
				lastPositionOperatingStyle = activeClass;
				lastPositionOperatingElement = e;
				$.Dom._class(e, '+' + activeClass);
			}
		}; 
		
		this.moveToTop = function (e, step, event) {
			if(event) $(event, 'stop');
			var tr = getTR(e), index = getIndex(tr), trs = $trs();
			if(index == 0) return;
			setModified();
			if(!step || step > index) step = index;
			var tdsHTML = $('td', tr).html(), posNum = hasPositionField && getPositionNum(tr);
			if (hasPositionField && posNum != -1) {
				for (var i = index - 1; i >= 0 && i >= (index - step); i--) {
					if (getPositionNum(trs[i]) !== -1) {
						setPositionNum(trs[i], posNum);
						posNum--;
					}
				}
			}
			var externInfo = getExternInfo(tr);
			tbody.deleteRow(index);
			var newTR = insertTR(tbody, index - step, tdsHTML, externInfo);
			positionOperating(newTR, 'up');
			if(hasPositionField && posNum !== -1) {
				setPositionNum(newTR, posNum);
			}
		};
		this.moveToBottom = function (e, step, event) {
			if(event) $(event, 'stop');
			var tr = getTR(e), index = getIndex(tr), trs = $trs();
			if (index == trs.length - 1) return;
			setModified();
			if (!step|| step > (trs.length - 1 - index)) {
				step = trs.length - 1 - index;	
			}
			var tdsHTML = $('td', tr).html(), posNum = hasPositionField && getPositionNum(tr);
			if (hasPositionField && posNum != -1) {
				for (var i = index + 1; i < trs.length && i <= (index + step); i++) {
					if (getPositionNum(trs[i]) !== -1) {
						setPositionNum(trs[i], posNum);
						posNum++;	
					}
				}
			}
			var externInfo = getExternInfo(tr);
			tbody.deleteRow(index);
			var newTR = insertTR(tbody, index + step, tdsHTML, externInfo);
			positionOperating(newTR, 'down');
			if (hasPositionField && posNum !== -1) {
				setPositionNum(newTR, posNum);	
			}
		};  
		
		/**
		 * 刷新位置域数值
		 */
		this.freshPositionNum = function () {
			if(hasPositionField) {
				var trs = $trs(), posNum = 1;
				for (var i = 0; i < trs.length; i++) {
					var tempNum = getPositionNum(trs[i]);
					if(sets.onFreshPositionNum) {
						if(!sets.onFreshPositionNum(trs[i])) {
							setPositionNum(trs[i], '');
						} else {
							setPositionNum(trs[i], posNum);
							posNum ++;
						}
						continue;	
					}
					if(tempNum == -1) continue;
					if (tempNum != posNum) setPositionNum(trs[i], posNum);
					posNum ++;	
				}	
			}
		};
		var getSerializeItem = function (tr) {
			var data = [];
			data.push(getID(tr));
			if(sets.serializePart) {
				for(var i = 0; i < sets.serializePart.length; i++) {
					data.push(sets.serializePart[i](tr));	
				}
			}
			return data.join('|');
		};
		
		var isWantToSerialize = sets.isWantToSerialize || function (tr, index) {
			return 1;	
		};
		/**
		 * 序列化记录
		 */
		this.serialize = function () {
			var trs = $trs(), data=[];
			for (var i = 0; i < trs.length; i++) {
				if (isWantToSerialize(trs[i], data.length + 1)) {
					data.push(getSerializeItem(trs[i]));	
				}
			}
			return data.join('\n');
		};
	}
});
